#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <microhttpd.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#define PORT 8080

// FFmpeg Global Variables
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
AVCodec *codec = NULL;
AVFrame *frame = NULL;
AVPacket packet;
struct SwsContext *sws_ctx = NULL;

// MicroHTTPD callback function to serve video data
static int send_video_data(void *cls, struct MHD_Connection *connection,
                            const char *url, const char *method,
                            const char *version, const char *upload_data,
                            size_t *upload_data_size, void **con_cls) {
  static int video_stream_idx = -1;
    if (strcmp(method, "GET") != 0)
        return MHD_NO; // Only handle GET requests

    // Initialize FFmpeg
    if (!fmt_ctx) {
  //      av_register_all();
  avdevice_register_all();
        avformat_network_init();
        
        // Open input stream (for example, video file or RTSP stream)
        if (avformat_open_input(&fmt_ctx, "source/test.mp4", NULL, NULL) < 0) {
            fprintf(stderr, "Could not open video file.\n");
            return MHD_NO;
        }
        
        if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
            fprintf(stderr, "Could not find stream information.\n");
            return MHD_NO;
        }

        // Find video stream
        int video_stream_idx = -1;
        for (int i = 0; i < fmt_ctx->nb_streams; i++) {
            if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                video_stream_idx = i;
                break;
            }
        }

        if (video_stream_idx == -1) {
            fprintf(stderr, "Could not find video stream.\n");
            return MHD_NO;
        }

        // Get codec context
        codec = avcodec_find_decoder(fmt_ctx->streams[video_stream_idx]->codecpar->codec_id);
        codec_ctx = avcodec_alloc_context3(codec);
        avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_idx]->codecpar);

        if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
            fprintf(stderr, "Could not open codec.\n");
            return MHD_NO;
        }

        // Initialize frame and packet
        frame = av_frame_alloc();
        av_init_packet(&packet);
    }

    // Read frame from video stream
    if (av_read_frame(fmt_ctx, &packet) < 0) {
        fprintf(stderr, "Error reading frame.\n");
        return MHD_NO;
    }

    if (packet.stream_index == video_stream_idx) {
        if (avcodec_send_packet(codec_ctx, &packet) < 0) {
            fprintf(stderr, "Error sending packet to codec.\n");
            return MHD_NO;
        }

        if (avcodec_receive_frame(codec_ctx, frame) == 0) {
            // Here, we would typically convert the frame to a format suitable for streaming
            // using sws_scale or any other format suitable for web streaming.
            
            // For simplicity, we assume raw YUV data or you could use a more complex encoding method.
            
            const char *response_header = "HTTP/1.1 200 OK\r\nContent-Type: video/mp4\r\n\r\n";
            struct MHD_Response *response = MHD_create_response_from_buffer(strlen(response_header), (void *)response_header, MHD_RESPMEM_PERSISTENT);
            MHD_add_response_header(response, "Content-Type", "video/mp4");

            // Send the response
            int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
            MHD_destroy_response(response);
            return ret;
        }
    }

    av_packet_unref(&packet);
    return MHD_NO;
}

int main() {
    struct MHD_Daemon *daemon;

    // Start HTTP server using microhttpd
    daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, PORT, NULL, NULL, &send_video_data, NULL, MHD_OPTION_END);
    if (daemon == NULL) {
        fprintf(stderr, "Failed to start HTTP server.\n");
        return 1;
    }

    printf("Server started on port %d...\n", PORT);
    getchar(); // Wait for user input to stop the server

    MHD_stop_daemon(daemon);
    avformat_close_input(&fmt_ctx);
    avcodec_free_context(&codec_ctx);
    av_frame_free(&frame);
    return 0;
}

